package es.gestores;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;

import es.entidades.Entidad;

/**
 * Gestiona las entidades.
 * @author Miguel Vicente
 */
public class GestorEntidades {
	
	private HashMap<String, ArrayList<Entidad>> entidadesPorTipo;
	private ArrayList<GestorEntidadesListener> listeners;
	
	/**
	 * Constructor. Crea un nuevo GestorEntidades
	 */
	public GestorEntidades() {
		this.entidadesPorTipo = new HashMap<String, ArrayList<Entidad>>();
		this.listeners = new ArrayList<GestorEntidadesListener>();
	}
	/**
	 * Devuelve un ArrayList con todas las entidades del tipo especificado.
	 * @param tipo
	 * @return ArrayList
	 */
	public ArrayList<Entidad> getEntidadesTipo(String tipo) {
		ArrayList<Entidad> entidadesMismoTipo = entidadesPorTipo.get(tipo);
		if (entidadesMismoTipo == null) {
			entidadesMismoTipo = new ArrayList<Entidad>();
			entidadesPorTipo.put(tipo, entidadesMismoTipo);
		}
		return entidadesMismoTipo;
	}
	/**
	 * Agrega una entidad al gestor de entidades si no habia sido agregada previamente.
	 * @param entidad Entidad a agregar
	 */
	public void agregarEntidad(Entidad entidad) {
		ArrayList<Entidad> entidadesMismoTipo;
		boolean encontrada = false;
		if (entidad == null) {
			return;
		}
		entidadesMismoTipo = getEntidadesTipo(entidad.getTipo());
		for (int i = 0; i < entidadesMismoTipo.size(); i++) {
			if (entidadesMismoTipo.get(i).getId() == entidad.getId()) {
				encontrada = true;
			}
		}
		if (!encontrada) {
			entidadesMismoTipo.add(entidad);
			notificarEntidadAgregada(entidad);
		}
	}
	/**
	 * Elimina una entidad del gestor de entidades.
	 * @param entidad Entidad a eliminar
	 */
	public void eliminarEntidad(Entidad entidad) {
		ArrayList<Entidad> entidadesMismoTipo;
		if (entidad == null) {
			return;
		}
		entidadesMismoTipo = getEntidadesTipo(entidad.getTipo());
		for (int i = 0; i < entidadesMismoTipo.size(); i++) {
			if (entidadesMismoTipo.get(i).getId() == entidad.getId()) {
				entidadesMismoTipo.remove(i);
				notificarEntidadAgregada(entidad);
			}
		}
		// Si no quedan entidades de ese tipo, eliminamos la entrada del mapa.
		if (entidadesMismoTipo.isEmpty()) {
			eliminarEntrada(entidad.getTipo());
		}
	}
	/**
	 * Elimina una entrada del mapa de entidades.
	 * @param tipo Tipo que identifica la entrada a eliminar
	 */
	private void eliminarEntrada(String tipo) {
		for (Iterator<Map.Entry<String, ArrayList<Entidad>>> it = entidadesPorTipo.entrySet().iterator(); it.hasNext();)	{
			Map.Entry<String, ArrayList<Entidad>> entrada = it.next();
			if (entrada.getKey() == tipo) {
		    	ArrayList<Entidad> entidadesEntrada = entrada.getValue();
		    	if (entidadesEntrada != null) {
					for (int i = 0; i < entidadesEntrada.size(); i++) {
						notificarEntidadEliminada(entidadesEntrada.get(i));
						entidadesEntrada.remove(i);
					}
				}
		        it.remove();  
		    }
		}
	}
	/**
	 * Elimina todas las entidades del gestor.
	 */
	public void eliminarTodo() {
		Iterator<Entry<String, ArrayList<Entidad>>> it = entidadesPorTipo.entrySet().iterator();
		while (it.hasNext()) {
			Map.Entry<String, ArrayList<Entidad>> e = (Map.Entry<String, ArrayList<Entidad>>)it.next();
			ArrayList<Entidad> entidades = e.getValue();
			for (int i = 0; i < entidades.size(); i++) {
				notificarEntidadEliminada(entidades.get(i));
				entidades.remove(i);
			}
			it.remove();
		}
	}
	/**
	 * Devuelve una entidad a partir de su ID.<br>
	 * Si no se encuentra devuelve null.
	 * @param id ID de la entidad a buscar
	 * @return Entidad
	 */
	public Entidad getEntidad(int id) {
		if (!entidadesPorTipo.isEmpty()) {
			Iterator<Entry<String, ArrayList<Entidad>>> it = entidadesPorTipo.entrySet().iterator();
			while (it.hasNext()) {
				Map.Entry<String, ArrayList<Entidad>> e = (Map.Entry<String, ArrayList<Entidad>>)it.next();
				ArrayList<Entidad> entidades = e.getValue();
				for (Entidad entidad : entidades) {
					if (entidad.getId() == id) {
						return entidad;
					}
				}
			}
		}
		return null;
	}
	/**
	 * Devuelve un {@code HashMap<String, ArrayList<Entidad>>} con todas las entidades del gestor 
	 * indexadas por tipo.
	 * @return {@code HashMap<String, ArrayList<Entidad>>}
	 */
	public HashMap<String, ArrayList<Entidad>> getEntidadesPorTipo() {
		return entidadesPorTipo;
	}
	/**
	 * Pone en escucha un GestorEntidadesListener
	 * @param listener GestorEntidadesListener
	 */
	public void addGestorEntidadesListener(GestorEntidadesListener listener) {
		listeners.add(listener);
	}
	/**
	 * Notifica a todos los GestorEntidadesListener que una Entidad ha sido agregada al gestor.
	 * @param entidad Entidad agregada
	 */
	public void notificarEntidadAgregada(Entidad entidad) {
		for (GestorEntidadesListener listener : listeners) {
			listener.entidadAgregada(entidad);
		}
	}
	/**
	 * Notifica a todos los GestorEntidadesListener que una Entidad ha sido eliminada del gestor.
	 * @param entidad Entidad eliminada
	 */
	public void notificarEntidadEliminada(Entidad entidad) {
		for (GestorEntidadesListener listener : listeners) {
			listener.entidadEliminada(entidad);
		}
	}
		
	@Override
	public String toString() {
		String s = "---------------GestorEntidades---------------\n";
		s += "\n----------Entidades por tipo:\n";
		if (!entidadesPorTipo.isEmpty()) {
			Iterator<Entry<String, ArrayList<Entidad>>> it = entidadesPorTipo.entrySet().iterator();
			while (it.hasNext()) {
				Map.Entry<String, ArrayList<Entidad>> e = (Map.Entry<String, ArrayList<Entidad>>)it.next();
				s += "-Tipo: " + e.getKey() + "\n";
				ArrayList<Entidad> ents = e.getValue();
				for (int i = 0; i < ents.size(); i++) {
					s += ents.get(i) + "\n";
				}
			}
		}
		s += "---------------------------------------------\n";
		return s;
	}	
	
}
